#include <iostream>
#include <cassert>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define MAX 1024

using namespace std;

// a. 管道的4种情况
//    1. 正常情况，如果管道没有数据了，读端必须等待，直到有数据为止(写端写入数据了)
//    2. 正常情况，如果管道被写满了，写端必须等待，直到有空间为止(读端读走数据)
//    3. 写端关闭，读端一直读取, 读端会读到read返回值为0， 表示读到文件结尾
//    4. 读端关闭，写端一直写入，OS会直接杀掉写端进程，通过想目标进程发送SIGPIPE(13)信号，终止目标进程 ---- 如何证明？？
// b. 管道的5种特性
//    1. 匿名管道,可以允许具有血缘关系的进程之间进行进程间通信，常用与父子,仅限于此
//    2. 匿名管道，默认给读写端要提供同步机制 --- 了解现象就行
//    3. 面向字节流的 --- 了解现象就行
//    4. 管道的生命周期是随进程的
//    5. 管道是单向通信的，半双工通信的一种特殊情况


int main()
{
    int pipefd[2];
    int n = pipe(pipefd);
    assert(n == 0);
    (void)n; // 防止编译器告警，意料之中，用assert，意料之外，用if
    cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << endl;

    //创建子进程
    pid_t id = fork();
    if(id < 0){
        perror("error");
        exit(1);
    }
    //子进程
    if(id == 0){
        // if(fork() > 0) exit(0); //
        // child
        close(pipefd[0]);
        // w - 只向管道写入，没有打印
        int cnt = 0;
        while(true)
        {
            // char c = 'a';
            // write(pipefd[1], &c, 1);
            // cnt++;
            // cout << "write ....: " << cnt << endl; // 课堂上我们的机器的pipe空间大小是64KB
            
            char message[MAX];
            snprintf(message, sizeof(message), "hello father, I am child, pid: %d, cnt: %d", getpid(), cnt);
            cnt++;
            write(pipefd[1], message, strlen(message));
            sleep(1);

             if(cnt > 3) break;
        }
        cout << "child close w piont" << endl;
        // close(pipefd[1]);
        exit(0);
    }
    
    //父进程
    close(pipefd[1]);
    char buffer[MAX];
    while(true){
        ssize_t n = read(pipefd[0], buffer, sizeof(buffer)-1);
        if(n > 0){
            buffer[n] = 0; // '\0', 当做字符串
            cout << getpid() << ", " << "child say: " << buffer << " to me!" << endl;
        }else if(n == 0){
             cout << "child quit, me too !" << endl;
            break;
        }
          cout << "father return val(n): " << n << endl;
        sleep(1);

        break;
    }
    sleep(5);
     cout << "read point close"<< endl;
    close(pipefd[0]);

    sleep(5);
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid == id)
    {
        cout << "wait success, child exit sig: " << (status&0x7F) << endl;
    }
    return 0;
}
